package com.springboot.ww.platform.pd.ctms.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.springboot.ww.platform.pd.ctms.api.*;
import com.springboot.ww.platform.pd.ctms.constants.PdApiConstants;
import com.springboot.ww.platform.pd.ctms.dto.PdForecastDTO;
import com.springboot.ww.platform.pd.ctms.service.IPdProxy;
import com.springboot.ww.platform.pd.dto.PdStoreTokenVO;
import com.springboot.ww.platform.pd.dto.Record;
import com.springboot.ww.platform.pd.http.PdHttpProxy;
import com.springboot.ww.platform.pd.http.ResponseMsg;
import com.springboot.ww.platform.pd.util.SignUtil;
import com.ww.springboot.boot.utils.AssertUtil;
import com.ww.springboot.boot.utils.Func;
import com.ww.springboot.boot.utils.json.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.springframework.stereotype.Service;

import java.nio.charset.Charset;
import java.util.*;

import static cn.hutool.core.util.ClassUtil.isSimpleValueType;

/**
 * 描述：
 *
 * @author 🧑 ‍wanwei
 * @since 2024-05-13 18:11
 */
@Service
@AllArgsConstructor
@Slf4j
public class PdProxyImpl implements IPdProxy {

    private static final String URL_FORMAT = "https://%s/openapi/router";
    public final static String US_DOMAIN = "openapi-b-us.temu.com";
    public final static String US_OTHER_DOMAIN = "openapi-b-global.temu.com";
    public final static String EU_DOMAIN = "openapi-b-eu.temu.com";

    public final static String TOKEN_INVALID_MSG = "access_token is expired or have been refreshed";
    public final static String TOKEN_INVALID_MSG2 = "app_key don't have this api permission";

    public String getDoMainByRegionId(int regionId) {
        switch (regionId) {
            case 211:
            case 164:
                return US_DOMAIN;
            case 37:
                return US_OTHER_DOMAIN;
            default:
                throw new RuntimeException("regionId未配置域名,请联系管理员");
        }
    }

    public String getDoMainByPlatOrderId(String platOrderId) {
        return getDoMainByRegionId(Integer.valueOf(platOrderId.substring(3, 6)));
    }

    public final static Map<String, List<String>> warehouseNameMap = new LinkedHashMap() {{
        put("CK0569", Arrays.asList("GC-MD", "GUCANG-MD", "谷仓-美东", "GUCANGMEIDONG", "美东11号", "谷仓11号"));
        put("HWC530630", Arrays.asList("GC-MX", "GUCANG-MX", "谷仓-美西", "美西5号", "谷仓5号", "GUCANGMEIXI"));
        put("USEAST1228", Arrays.asList("ZH-MD", "MEIDONG", "美东", "MD", "EAST"));
        put("USWEST1227", Arrays.asList("ZH-MX", "MEIXI", "美西", "MX", "WEST"));
    }};

    private final static String WAREHOUSE_KEY = "pd:warehouse:";

    @Override
    public String getWarehouseId(PdStoreTokenVO storeVO, String warehouseCode) {
        storeVO.setRealDoMain(getDoMainByRegionId(211));
        String key = WAREHOUSE_KEY + storeVO.getStoreCode();
        List<PdWarehouseResp.ResultBean.WarehouseListBean> warehouseListBeans = null;
        Map<String, Object> map = new TreeMap<>();
        this.usToken(map, storeVO, PdApiConstants.WAREHOUSE_GET);
        String resultResp = postUS(storeVO, JsonUtil.toJson(map));
        PdWarehouseResp pdWarehouseResp = JsonUtil.parse(resultResp, PdWarehouseResp.class);
        if (pdWarehouseResp == null
                || !pdWarehouseResp.isSuccess()
                || ObjectUtil.isEmpty(pdWarehouseResp.getResult())
                || ObjectUtil.isEmpty(pdWarehouseResp.getResult().getWarehouseList())) {
            throw new RuntimeException("拼多多仓库信息查询接口失败：" + resultResp);
        }
        warehouseListBeans = pdWarehouseResp.getResult().getWarehouseList();

        Optional<PdWarehouseResp.ResultBean.WarehouseListBean> optional = warehouseListBeans.stream()
                .filter(warehouseListBean -> warehouseCode.equals(this.getWarehouseCode(warehouseListBean.getWarehouseName())))
                .findAny();
        if (optional.isPresent()) {
            return optional.get().getWarehouseId();
        }
        throw new RuntimeException("拼多多仓库信息未找到包含【" + Optional
                .ofNullable(warehouseNameMap.get(warehouseCode)).map(s -> s.toString())
                .orElse(warehouseCode) + "】标识仓库：" + JsonUtil.toJson(warehouseListBeans));
    }

    private String getWarehouseCode(String warehouseName) {
        warehouseName = warehouseName.toUpperCase();
        for (Map.Entry<String, List<String>> entry : warehouseNameMap.entrySet()) {
            if (entry.getValue().stream().anyMatch(warehouseName::contains)) {
                return entry.getKey();
            }
        }
        return "";
    }


    @Override
    public List<PdChannelResp.ResultBean.OnlineChannelDtoListBean> getChannelList(PdStoreTokenVO storeVO, String platOrderId, PdChannelReq pdChannelReq) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(platOrderId));
        AssertUtil.notEmpty(pdChannelReq.getOrderSnList(), "拼多多平台子单号不能为空");
        Map<String, Object> map = new TreeMap<>();
        map.put("warehouseId", pdChannelReq.getWarehouseId());
        map.put("orderSnList", pdChannelReq.getOrderSnList());
        map.put("weight", pdChannelReq.getWeight().toString());
        map.put("weightUnit", pdChannelReq.getWeightUnit());
        if (ObjectUtil.isNotEmpty(pdChannelReq.getExtendWeight())) {
            map.put("extendWeight", pdChannelReq.getExtendWeight().toString());
            map.put("extendWeightUnit", "oz");
        }
        map.put("length", pdChannelReq.getLength().toString());
        map.put("width", pdChannelReq.getWidth().toString());
        map.put("height", pdChannelReq.getHeight().toString());
        map.put("dimensionUnit", pdChannelReq.getDimensionUnit());
        this.usToken(map, storeVO, PdApiConstants.SHIPPING_GET);
        String resultResp = postUS(storeVO, JsonUtil.toJson(map));
        PdChannelResp pdChannelResp = JsonUtil.parse(resultResp, PdChannelResp.class);
        if (pdChannelResp == null
                || !pdChannelResp.isSuccess()
                || ObjectUtil.isEmpty(pdChannelResp.getResult())) {
            throw new RuntimeException("拼多多可用渠道查询失败：" + resultResp);
        }
        return pdChannelResp.getResult().getOnlineChannelDtoList();
    }

    private String getRealApiUrl(String apiUrl) {
        if (ObjectUtil.isEmpty(apiUrl)) {
            return "4.242.79.188";
        }
        int index = apiUrl.lastIndexOf("//");
        if (index < 0) {
            index = 0;
        } else {
            index += 2;
        }
        apiUrl = apiUrl.substring(index);
        return apiUrl;
    }

    private String postUS(PdStoreTokenVO storeVO, String body) {
        String proxyHost = this.getRealApiUrl(storeVO.getApiUrl());
        HttpPost httpPost = new HttpPost(String.format(URL_FORMAT, ObjectUtil.isNotEmpty(storeVO.getRealDoMain()) ? storeVO.getRealDoMain() : US_DOMAIN));
        StringEntity reqEntity = new StringEntity(body, "UTF-8");
        reqEntity.setContentEncoding("UTF-8");
        reqEntity.setContentType("application/json");
        httpPost.setEntity(reqEntity);
        try {
            ResponseMsg responseMsg = PdHttpProxy.proxyWithRequest(proxyHost, httpPost);
            log.info("拼多多请求：" + proxyHost + "|" + body + " | " + JsonUtil.toJson(responseMsg));
            String regionName = "美区";
            if (US_OTHER_DOMAIN.equals(storeVO.getRealDoMain())) {
                regionName = "全球GLOBAL";
            }
            if (responseMsg.getStatus() != 200 || ObjectUtil.isEmpty(responseMsg.getResult())) {
                throw new RuntimeException("拼多多(" + regionName + ")接口调用失败：" + JsonUtil.toJson(responseMsg));
            } else if (responseMsg.getResult().contains(TOKEN_INVALID_MSG) || responseMsg.getResult().contains(TOKEN_INVALID_MSG2)) {
                throw new RuntimeException("拼多多(" + regionName + ")token失效：" + JsonUtil.toJson(responseMsg));
            }
            return responseMsg.getResult();
        } catch (Exception e) {
            log.info("拼多多请求异常：" + body + " | " + e.getMessage());
            throw e;
        }
    }

    @Override
    public String postForecast(PdForecastDTO pdForecastDTO, PdStoreTokenVO storeVO, Record record, Boolean shipLater) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(pdForecastDTO.getOrderSendInfoList().get(0).getParentOrderSn()));
        Map<String, Object> map = new TreeMap<>();
        map.put("sendType", "0");
        if (ObjectUtil.isNotEmpty(shipLater)) {
            map.put("shipLater", shipLater);
        }
        map.put("sendRequestList", Arrays.asList(pdForecastDTO));
        this.usToken(map, storeVO, PdApiConstants.FORECAST_CREATE);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdForecastResp pdForecastResp = JsonUtil.parse(resultResp, PdForecastResp.class);
        if (pdForecastResp == null) {
            throw new RuntimeException("拼多多物流下单接口失败：" + resultResp);
        }
        if (!pdForecastResp.isSuccess() && (pdForecastResp.getErrorMsg().equals("Order shipped") || pdForecastResp.getErrorMsg().equals("The current order has been bound to the delivery note"))) {
            //已下过单
            return null;
        }
        Optional<String> optional = Optional.ofNullable(pdForecastResp)
                .map(PdForecastResp::getResult)
                .map(resultBeanX -> resultBeanX.getPackageSnList())
                .map(list -> ObjectUtil.isNotEmpty(list) ? list.get(0) : null);
        if (optional.isPresent()) {
            return optional.get();
        }
        throw new RuntimeException("拼多多物流下单接口失败：" + resultResp);
    }

    @Override
    public PdTrackResp.ResultBean.PackageInfoResultListBean getShipmentResult(PdStoreTokenVO storeVO, String platformNumber, String packageSn, Record record) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(platformNumber));
        Map<String, Object> map = new TreeMap<>();
        map.put("packageSnList", Arrays.asList(packageSn));

        this.usToken(map, storeVO, PdApiConstants.FORECAST_GET);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdTrackResp pdTrackResp = JsonUtil.parse(resultResp, PdTrackResp.class);
        if (pdTrackResp == null || pdTrackResp.getResult() == null) {
            throw new RuntimeException("拼多多获取物流信息失败：" + resultResp);
        }
        if (ObjectUtil.isEmpty(pdTrackResp.getResult().getPackageInfoResultList())) {
            return null;
        }
        return pdTrackResp.getResult().getPackageInfoResultList().get(0);
    }

    @Override
    public String getLabelInfo(PdStoreTokenVO storeVO, String platformNumber, String packageSn, Record record) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(platformNumber));
        Map<String, Object> map = new TreeMap<>();
        map.put("packageSnList", Arrays.asList(packageSn));

        this.usToken(map, storeVO, PdApiConstants.FORECAST_DOCUMENT_GET);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdLabelResp pdLabelResp = JsonUtil.parse(resultResp, PdLabelResp.class);
        if (pdLabelResp == null) {
            throw new RuntimeException("拼多多获取物流面单信息失败：" + resultResp);
        }
        Optional<String> optional = Optional.ofNullable(pdLabelResp)
                .map(PdLabelResp::getResult)
                .map(resultBean -> ObjectUtil.isNotEmpty(resultBean.getShippingLabelUrlList()) ? resultBean.getShippingLabelUrlList() : null)
                .map(list -> list.get(0))
                .map(PdLabelResp.ResultBean.ShippingLabelUrlListBean::getUrl);
        if (optional.isPresent()) {
            return optional.get();
        }
        throw new RuntimeException("拼多多获取物流面单信息失败：" + resultResp);
    }

    @Override
    public List<PdOrderDetailResp.ResultBeanX.ResultBean.OrderListBean> getOrderDetail(PdStoreTokenVO storeVO, String platformNumber, Record record) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(platformNumber));
        Map<String, Object> map = new TreeMap<>();
        map.put("parentOrderSn", platformNumber);

        this.usToken(map, storeVO, PdApiConstants.GET_ORDER_DETAIL_LIST);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdOrderDetailResp orderDetailResp = JsonUtil.parse(resultResp, PdOrderDetailResp.class);
        if (orderDetailResp == null) {
            throw new RuntimeException("拼多多获取订单详情失败：" + resultResp);
        }
        Optional<List<PdOrderDetailResp.ResultBeanX.ResultBean.OrderListBean>> optional = Optional.ofNullable(orderDetailResp)
                .map(PdOrderDetailResp::getResult)
                .map(resultBean -> resultBean.getResult())
                .map(resultBean -> resultBean.getOrderList());
        if (optional.isPresent()) {
            return optional.get();
        }
        throw new RuntimeException("拼多多获取订单详情失败：" + resultResp);
    }

    @Override
    public PdShipmentResp.ResultBeanX.ResultBean.ShipmentInfoDTOBean getShipmentGet(PdStoreTokenVO storeVO, Record record, String parentOrderSn, String orderSn) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(parentOrderSn));
        Map<String, Object> map = new TreeMap<>();
        map.put("parentOrderSn", parentOrderSn);
        map.put("orderSn", orderSn);
        this.usToken(map, storeVO, PdApiConstants.SHIPMENT_GET);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdShipmentResp pdShipmentResp = JsonUtil.parse(resultResp, PdShipmentResp.class);
        if (pdShipmentResp == null || pdShipmentResp.getResult() == null || !pdShipmentResp.getResult().isSuccess()) {
            throw new RuntimeException("拼多多获取发货详情失败：" + resultResp);
        }
        if (ObjectUtil.isEmpty(pdShipmentResp.getResult().getResult())
                || ObjectUtil.isEmpty(pdShipmentResp.getResult().getResult().getShipmentInfoDTO())) {
            return null;
        }
        return pdShipmentResp.getResult().getResult().getShipmentInfoDTO().get(0);
    }

    @Override
    public byte[] getLabelSource(String url, String platformNumber, PdStoreTokenVO pdStoreTokenVO) {
        pdStoreTokenVO.setRealDoMain(getDoMainByPlatOrderId(platformNumber));
        HttpGet httpGet = new HttpGet(url);
        Map<String, Object> treeMap = new TreeMap<>();
        treeMap.put("toa-app-key", pdStoreTokenVO.getAppKey());
        if (US_DOMAIN.equals(pdStoreTokenVO.getRealDoMain())) {
            treeMap.put("toa-access-token", pdStoreTokenVO.getToken());
        } else if (US_OTHER_DOMAIN.equals(pdStoreTokenVO.getRealDoMain())) {
            treeMap.put("toa-access-token", ObjectUtil.isNotEmpty(pdStoreTokenVO.getTokenGlobal()) ? pdStoreTokenVO.getTokenGlobal() : pdStoreTokenVO.getToken());
        }

        treeMap.put("toa-random", UUID.randomUUID().toString().substring(4));
        treeMap.put("toa-timestamp", System.currentTimeMillis() / 1000);
        // 生成要签名的字符串
        StringBuilder signText = new StringBuilder();
        treeMap.entrySet().forEach(entry -> {
            if (isSimpleValueType(entry.getValue().getClass())) {
                signText.append(entry.getKey()).append(entry.getValue());
            } else {
                signText.append(entry.getKey()).append(JsonUtil.toJson(entry.getValue()));
            }
        });
        String s = pdStoreTokenVO.getAppSecret() + signText + pdStoreTokenVO.getAppSecret();
        for (Map.Entry<String, Object> entry : treeMap.entrySet()) {
            httpGet.setHeader(entry.getKey(), String.valueOf(entry.getValue()));
        }
        httpGet.setHeader("toa-sign", Func.md5Hex(s).toUpperCase());
        byte[] bytes = PdHttpProxy.proxyByteWithRequest(this.getRealApiUrl(pdStoreTokenVO.getApiUrl()), httpGet);
        if (bytes.length < 1000) {
            throw new RuntimeException("获取面单资源失败：" + new String(bytes, Charset.defaultCharset()));
        }
        //图片转pdf
        return bytes;
    }

    private void usToken(Map<String, Object> map, PdStoreTokenVO storeVO, String api) {
        map.put("data_type", "JSON");
        if (US_DOMAIN.equals(storeVO.getRealDoMain())) {
            map.put("access_token", storeVO.getToken());
        } else if (US_OTHER_DOMAIN.equals(storeVO.getRealDoMain())) {
            map.put("access_token", ObjectUtil.isNotEmpty(storeVO.getTokenGlobal()) ? storeVO.getTokenGlobal() : storeVO.getToken());
        }
        String sign = SignUtil.pd(map, storeVO.getAppKey(), storeVO.getAppSecret(), api);
        map.put("sign", sign);
    }

    @Override
    public List<PdUnShipPackageResp.ResultDTO.UnshippedPackageDTO> getUnShipPackageResp(PdStoreTokenVO storeVO, String platformNumber, Record record) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(platformNumber));
        List<PdUnShipPackageResp.ResultDTO.UnshippedPackageDTO> list = new ArrayList<>();
        int pageNumber = 1;
        while (true) {
            Map<String, Object> map = new TreeMap<>();
            map.put("pageNumber", pageNumber);
            map.put("pageSize", "50");
            usToken(map, storeVO, PdApiConstants.UN_SHIPPED_PACKAGE_GET);
            String req = JsonUtil.toJson(map);
            String resultResp = postUS(storeVO, req);
            PdUnShipPackageResp resp = JsonUtil.parse(resultResp, PdUnShipPackageResp.class);
            if (resp == null || !resp.getSuccess()) {
                throw new RuntimeException("待发货列表查询失败：" + resultResp);
            }
            if (ObjectUtil.isEmpty(resp.getResult().getUnshippedPackage())) {
                return list;
            }
            list.addAll(resp.getResult().getUnshippedPackage());
            pageNumber++;
        }
    }

    @Override
    public void updateForecast(PdForecastDTO pdForecastDTO, PdStoreTokenVO storeVO, Record record) {
        storeVO.setRealDoMain(getDoMainByPlatOrderId(pdForecastDTO.getOrderSendInfoList().get(0).getParentOrderSn()));
        Map<String, Object> map = new TreeMap<>();
        map.put("retrySendPackageRequestList", Arrays.asList(pdForecastDTO));
        this.usToken(map, storeVO, PdApiConstants.FORECAST_UPDATE);
        String req = JsonUtil.toJson(map);
        record.setCommonReq(req);
        String resultResp = postUS(storeVO, req);
        record.setCommonResp(resultResp);
        PdForecastUpdateResp forecastUpdateResp = JsonUtil.parse(resultResp, PdForecastUpdateResp.class);
        if (forecastUpdateResp == null || !forecastUpdateResp.isSuccess()) {
            throw new RuntimeException("拼多多物流下单更新接口失败：" + resultResp);
        }
    }
}
